
use std::sync::{Arc, Mutex};
use std::ops::Deref;
use rusqlite::{Connection, params, named_params};

use super::Config;
use super::{User, UserValue, Error};

const COOKIE_LEN: u32 = 64;     /// session id的长度

/// 用于每一个线程的上下文
pub struct Context {
    pub config: Arc<Mutex<Config>>,
    pub db: rusqlite::Connection,
}

impl Context {
    /// 创建一个Context
    /// config: 全局配置信息
    /// do_init: 是否初始化data目录。如果为false而data未初始化，则返回Err
    pub fn create(config: Arc<Mutex<Config>>) -> Result<Context, Error> {
        // 获取锁
        let config_data = match config.lock() {
            Ok(g) => g,
            Err(e) =>
                return Err(Error::LockError(String::from(e.to_string())))?,
        };

        // 检查web路径是否存在
        if config_data.host_web && !config_data.web_path.exists() {
            Err(Error::StorageError(String::from("web path does not exist")))?;
        }

        // 检查data路径是否存在
        if !config_data.data_path.exists() {
            Err(Error::StorageError(String::from("data path does not exist")))?;
        }

        // 检查data目录是否初始化
        if let Err(e) = Context::check_data_path(config_data.deref()) {
            if config_data.do_init {
                // 尝试初始化data目录
                Context::init_data_path(config_data.deref())?;
            } else {
                // data目录没初始化
                Err(e)?;
            }
        }

        let mut db_path = config_data.data_path.clone();
        db_path.push("main.db");

        // 释放锁
        std::mem::drop(config_data);

        Ok(Context {
            config,
            db: Connection::open(db_path)?,
        })
    }


    /// 检查data目录是否初始化
    fn check_data_path(config: &Config) -> Result<(), Error> {

        // 检查/data/main.db是否存在
        let mut db_path = config.data_path.clone();
        db_path.push("main.db");
        if !db_path.exists() || !db_path.is_file() {
            Err(Error::StorageError(String::from("sqlite file missing")))?;
        }
        // 检查/data/main.db是否是一个有效的sqlite文件
        {
            use rusqlite::OpenFlags;
            match Connection::open_with_flags(&db_path,
                OpenFlags::SQLITE_OPEN_READ_ONLY) {
                Ok(mut db) => Context::check_database(&mut db)?,   // 检查数据库是否正确
                Err(e) => Err(e)?,
            };
        }

        // 检查/data/refs/是否存在
        let mut refs_path = config.data_path.clone();
        refs_path.push("refs");
        if !refs_path.exists() || !refs_path.is_dir() {
            Err(Error::StorageError(String::from("/data/refs missing")))?;
        }

        // 检查/data/files/是否存在
        let mut files_path = config.data_path.clone();
        files_path.push("files");
        if !files_path.exists() || !files_path.is_dir() {
            Err(Error::StorageError(String::from("/data/files missing")))?;
        }

        Ok(())
    }


    /// 对data目录进行初始化
    fn init_data_path(config: &Config) -> Result<(), Error> {
        // 初始化数据库文件
        let mut db_path = config.data_path.clone();
        db_path.push("main.db");
        if db_path.exists() {
            if db_path.is_file() {
                std::fs::remove_file(&db_path)?;
            } else if db_path.is_dir() {
                std::fs::remove_dir_all(&db_path)?;
            }
        }
        let mut db_con = Connection::open(&db_path)?;
        Context::init_database(&mut db_con)?;

        // 新建refs目录
        let mut refs_path = config.data_path.clone();
        refs_path.push("refs");
        if !refs_path.exists() {
            std::fs::create_dir_all(&refs_path)?;
        }

        // 新建files目录
        let mut files_path = config.data_path.clone();
        files_path.push("files");
        if !files_path.exists() {
            std::fs::create_dir_all(&files_path)?;
        }

        Ok(())
    }


    /// 初始化数据库
    fn init_database(db_con: &mut rusqlite::Connection) -> Result<(), Error> {

        // 设置数据库编码
        db_con.execute(
            "PRAGMA encoding = \"UTF-8\";",
            params![])?;

        // 创建user表
        db_con.execute(
            "CREATE TABLE user (
                id      INT PRIMARY KEY,
                name    TEXT NOT NULL UNIQUE,
                password    TEXT NOT NULL,
                salt    TEXT NOT NULL,
                create_date BIGINT NOT NULL,
                is_admin    TINYINT NOT NULL DEFAULT 0,
                has_avatar  TINYINT NOT NULL DEFAULT 0,
                avatar  BLOB DEFAULT NULL,
                is_deleted  TINYINT NOT NULL DEFAULT 0,
                delete_date BIGINT NOT NULL DEFAULT 0
            );",
                params![])?;

        // 创建分类表
        db_con.execute(
            "CREATE TABLE catalog (
                id  INT PRIMARY KEY,
                name    TEXT NOT NULL UNIQUE,
                create_date BIGINT NOT NULL,
                modified_date   BIGINT NOT NULL,
                user_id INT,
                count   INT NOT NULL DEFAULT 0,
                is_delete   TINYINT NOT NULL DEFAULT 0,
                delete_date INT NOT NULL,
                FOREIGN KEY(user_id) REFERENCES user(id)
            );",
            params![])?;

        // 创建文章表
        db_con.execute(
            "CREATE TABLE article (
                id  INT PRIMARY KEY,
                title TEXT NOT NULL,
                content LONGTEXT NOT NULL,
                type TINYINT NOT NULL,
                user_id INT NOT NULL,
                catalog_id INT NOT NULL,
                create_date BIGINT NOT NULL,
                modified_date BIGINT NOT NULL,
                is_deleted TINYINT NOT NULL DEFAULT 0,
                deleted_date INT NOT NULL,
                FOREIGN KEY(user_id) REFERENCES user(id),
                FOREIGN KEY(catalog_id) REFERENCES catalog(id)
            );",
            params![])?;

        // 创建cookie表
        db_con.execute(
            "CREATE TABLE cookie (
                cookie TEXT NOT NULL,
                user_id INT NOT NULL,
                login_date BIGINT NOT NULL,
                expir_date BIGINT NOT NULL,
                FOREIGN KEY(user_id) REFERENCES user(id)
            );",
            params![]
        )?;

        Ok(())
    }


    /// 检查数据库是否满足需求
    fn check_database(db: &mut rusqlite::Connection) -> Result<(), Error> {
        use rusqlite::NO_PARAMS;

        // ----- user -----
        // user表所需要的列
        let user_column_needed = ["id", "name", "password", "salt", "create_date", "is_admin",
            "has_avatar", "avatar", "is_deleted", "delete_date"];
        // user表所查询出来的列
        let mut user_column_queried: Vec<String> = Vec::new();
        let mut query_user = db.prepare(
            "SELECT name FROM pragma_table_info('user')")?;
        let rows_user = query_user.query_map(NO_PARAMS,
            |row| row.get(0) )?;
        for row in rows_user {
            user_column_queried.push(row?);
        }
        // 检查所需的列是否存在
        for n in user_column_needed.iter() {
            let mut found = false;
            for q in user_column_queried.iter() {
                if q == n {
                    found = true;
                    break;
                }
            }
            if !found {
                Err(Error::DatabaseError(String::from("column not found")))?;
            }
        }

        // ----- catalog -----
        let catalog_column_needed = ["id", "name", "create_date", "modified_date",
            "user_id", "count", "is_deleted", "delete_date"];
        let mut catalog_column_queried: Vec<String> = Vec::new();
        let mut query_catalog = db.prepare(
            "SELECT name FROM pragma_table_info('catalog')")?;
        let rows_catalog = query_catalog.query_map(NO_PARAMS,
                                                 |row| row.get(0) )?;
        for row in rows_catalog {
            catalog_column_queried.push(row?);
        }
        for n in catalog_column_needed.iter() {
            let mut found = false;
            for q in catalog_column_queried.iter() {
                if q == n {
                    found = true;
                    break;
                }
            }
            if !found {
                Err(Error::DatabaseError(String::from("column not found")))?;
            }
        }

        // ----- article -----
        let article_column_needed = ["id", "title", "content", "type", "user_id",
            "catalog_id", "create_date", "modified_date", "is_deleted", "delete_date"];
        let mut article_column_queried: Vec<String> = Vec::new();
        let mut query_article = db.prepare(
            "SELECT name FROM pragma_table_info('article')")?;
        let rows_article = query_article.query_map(NO_PARAMS,
                                                       |row| row.get(0) )?;
        for row in rows_article {
            article_column_queried.push(row?);
        }
        for n in article_column_needed.iter() {
            let mut found = false;
            for q in article_column_queried.iter() {
                if q == n {
                    found = true;
                    break;
                }
            }
            if !found {
                Err(Error::DatabaseError(String::from("column not found")))?;
            }
        }

        // ----- cookie -----
        let cookie_column_needed = ["cookie", "user_id", "login_date", "expir_date"];
        let mut cookie_column_queried: Vec<String> = Vec::new();
        let mut query_cookie = db.prepare(
            "SELECT name FROM pragma_table_info('cookie')")?;
        let rows_cookie = query_cookie.query_map(NO_PARAMS,
                                                       |row| row.get(0) )?;
        for row in rows_cookie {
            cookie_column_queried.push(row?);
        }
        for n in cookie_column_needed.iter() {
            let mut found = false;
            for q in cookie_column_queried.iter() {
                if q == n {
                    found = true;
                    break;
                }
            }
            if !found {
                Err(Error::DatabaseError(String::from("column not found")))?;
            }
        }

        Ok(())
    }


    /// 检查该cookie对应的用户是否为管理员
    fn check_admin(&mut self, cookie: &str) -> Result<User, Error> {
        Ok(self.db.query_row_named(
            "SELECT user.id, user.name, user.password, user.salt,
                user.create_date, user.is_admin, user.avatar
            FROM user, cookie
            WHERE cookie.cookie == :cookie AND user.id == cookie.user_id AND user.is_admin == 1",
            named_params!{":cookie": &cookie},
            |row| {
                Ok(User {
                    id: row.get(0)?,
                    name: row.get(1)?,
                    password: row.get(2)?,
                    salt: row.get(3)?,
                    create_date: row.get(4)?,
                    is_admin: row.get(5)?,
                    avatar: {
                        if let Ok(avatar) = row.get(6) {
                            Some(avatar)
                        } else {
                            None
                        }
                    },
                    delete_date: None,
                })
            }
        )?)
    }


    /// 对明文密码使用加密盐混淆后使用sha3-256算法进行hash，返回hash后的密码和加密盐
    fn gen_password(password: &str) -> Result<(String, String), Error> {
        use sha3::{Digest, Sha3_256};
        let salt = super::gen_random_str(16);
        let mut password_mix = String::new();
        password_mix.push_str(password);
        password_mix.push_str(&salt);
        let mut hasher = Sha3_256::new();
        hasher.input(password_mix.as_bytes());
        let hash_res = String::from_utf8(Vec::from(hasher.result().as_slice()))?;
        Ok((hash_res, salt))
    }


    /// 检查明文密码password与使用加密盐salt混淆后进行sha3-256的原始密码hash是否匹配
    fn check_password(password: &str, hash: &str, salt: &str) -> Result<bool, Error> {
        use sha3::{Digest, Sha3_256};
        let ori = String::from(password) + salt;
        let mut hasher = Sha3_256::new();
        hasher.input(ori.as_bytes());
        let hash_res = String::from_utf8(Vec::from(hasher.result().as_slice()))?;
        if hash_res.eq(hash) {
            return Ok(true);
        }
        Ok(false)
    }


    /// 生成一个随机的唯一cookie
    fn gen_cookie(&mut self) -> Result<String, Error> {
        let tx = self.db.transaction()?;
        let mut random_str;
        let mut retry_times = 0;
        loop {
            random_str = super::gen_random_str(COOKIE_LEN);
            let res = tx.query_row_named(
                "SELECT FROM cookie
                WHERE cookie == :cookie",
                named_params!{":cookie": &random_str}, |_| Ok(()));
            match res {
                Err(rusqlite::Error::QueryReturnedNoRows) => return Ok(random_str),
                Err(e) => Err(e)?,
                Ok(_) => {
                    retry_times += 1;
                    // 如果尝试了1000次都失败了说明cookie用完了（几乎不可能）
                    if retry_times >= 1000 {
                        break;
                    }
                    continue;
                },
            };
        }
        tx.rollback()?;
        Err(Error::DatabaseError(String::from("failed to gen cookie")))
    }
}


/// 公开的接口
impl Context {
    /// 添加用户
    pub fn user_add(&mut self, name: &str, password: &str)
                    -> Result<(), Error> {
        // 将明文密码进行sha256加密
        let (password_hash, salt) = Context::gen_password(&password)?;
        let rows = self.db.execute_named(
            "INSERT INTO user (id, name, password, salt, create_date)
            VALUE ((SELECT MAX(id) FROM user)+1, :name, :password, :salt, :create_date)",
            named_params!{":name": &name, ":password": &password_hash, ":salt": &salt,
                ":create_date": &0})?;
        if rows == 0 {
            Err(Error::DatabaseError(String::from("add user failed")))?;
        }
        Ok(())
    }


    /// 查询用户
    pub fn user_query(&mut self, uid: u32) -> Result<User, Error> {
        let mut user_stmt = self.db.prepare(
            "SELECT id, name, password, salt, create_date, is_admin, avatar
            FROM user
            WHERE id == :id")?;

        let mut user_query = user_stmt.query_map_named(&[(":id", &uid)],
            |row| {
                    Ok(User {
                        id: row.get(0)?,
                        name: row.get(1)?,
                        password: row.get(2)?,
                        salt: row.get(3)?,
                        create_date: row.get(4)?,
                        is_admin: row.get(5)?,
                        avatar: {
                            if let Ok(avatar) = row.get(6) {
                                // 有头像
                                Some(avatar)
                            } else {
                                // 无头像
                                None
                            }
                        },
                        delete_date: None,
                    })
            })?;

        match user_query.next() {
            Some(r) => return Ok(r?),
            None => Err(Error::DatabaseError(String::from("")))?,
        }
    }


    /// 查询用户
    pub fn user_query_by_name(&mut self, name: &str) -> Result<User, Error> {
        let mut user_stmt = self.db.prepare(
            "SELECT id, name, password, salt, create_date, is_admin, avatar
            FROM user
            WHERE name == :name")?;

        let mut user_query = user_stmt.query_map_named(&[(":name", &name)],
            |row| {
                    Ok(User {
                        id: row.get(0)?,
                        name: row.get(1)?,
                        password: row.get(2)?,
                        salt: row.get(3)?,
                        create_date: row.get(4)?,
                        is_admin: row.get(5)?,
                        avatar: {
                            if let Ok(avatar) = row.get(6) {
                                // 有头像
                                Some(avatar)
                            } else {
                                // 无头像
                                None
                            }
                        },
                        delete_date: None,
                    })
            })?;

        match user_query.next() {
            Some(r) => return Ok(r?),
            None => Err(Error::DatabaseError(String::from("failed to query user")))?,
        }
    }


    /// user_delete
    pub fn user_delete(&mut self, uid: u32) -> Result<User, Error> {
        let user = self.user_query(uid)?;
        let rows = self.db.execute_named(
            "DELETE FROM user
            WHERE id == :id", named_params!{":id": &user.id})?;
        if rows == 0 {
            Err(Error::DatabaseError(String::from("failed to delete user")))?;
        }

        Ok(user)
    }


    /// 设置用户属性值
    pub fn user_set(&mut self, uid: u32, value: UserValue) -> Result<(), Error> {
        let rows = match value {
            UserValue::Name(ref v) => {
                self.db.execute_named(
                    "UPDATE user
                    SET name = :name
                    WHERE id == :id", named_params!{":name": v, ":id": &uid})?
            },
            UserValue::IsAdmin(v) => {
                self.db.execute_named(
                    "UPDATE user
                    SET is_admin = :is_admin
                    WHERE id == :id", named_params!{":is_admin": v, ":id": &uid})?
            },
            UserValue::Avatar(ref v) => {
                self.db.execute_named(
                    "UPDATE user
                    SET avatar = :avatar
                    WHERE id == :id", named_params!{":avatar": v, ":id": &uid})?
            },
        };

        if rows == 0 {
            Err(Error::DatabaseError(String::from("failed to set data")))?;
        }

        Ok(())
    }


    /// 用户登陆，成功返回用户的信息和session id
    pub fn user_login(&mut self, uid: u32, password: &str, expir_date: i64)
        -> Result<(User, String), Error> {
        let user = self.user_query(uid)?;
        if !Context::check_password(password, &user.password, &user.salt)? {
            Err(Error::DatabaseError(String::from("faild to login")))?;
        }

        let cookie = self.gen_cookie()?;
        let cur_date: i64 = super::get_current_timestamp();

        let rows = self.db.execute_named(
            "INSERT INTO cookie (cookie, user_id, login_date, expir_date)
            VALUE (:cookie, :uid, :login_date, :expir_date)",
            named_params!{
                ":cookie": &cookie,
                ":uid": &user.id,
                ":login_date": &cur_date,
                ":expir_date": &(cur_date + expir_date)})?;
        if rows == 0 {
            Err(Error::DatabaseError(String::from("failed to login")))?;
        }
        
        Ok((user, cookie))
    }


    /// 用户注销
    pub fn user_logout(&mut self, cookie: &str) -> Result<(), Error> {
        let rows = self.db.execute_named(
            "DELETE FROM cookie
            WHERE cookie == :cookie", named_params!{":cookie": cookie})?;
        if rows == 0 {
            Err(Error::DatabaseError(String::from("failed to delete cookie")))?
        }
        Ok(())
    }


    /// 用户验证
    pub fn user_verify(&mut self, cookie: &str) -> Result<User, Error> {
        let user = self.db.query_row_named(
            "SELECT user.id, user.name, user.password, user.salt, user.create_date, user.is_admin, user.avatar
            FROM user, cookie
            WHERE cookie.cookie == :cookie AND user.id == cookie.user_id",
            named_params!{":cookie": cookie},
            |row| {
                Ok(User {
                    id: row.get(0)?,
                    name: row.get(1)?,
                    password: row.get(2)?,
                    salt: row.get(3)?,
                    create_date: row.get(4)?,
                    is_admin: row.get(5)?,
                    avatar: {
                        if let Ok(avatar) = row.get(6) {
                            Some(avatar)
                        } else {
                            None
                        }
                    },
                    delete_date: None,
                })
            })?;
        Ok(user)
    }
}